﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Messaging;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using log4net;
using log4net.Config;

namespace Core
{
    public class MessagingService
    {
        private static readonly ILog logger = LogManager.GetLogger(typeof(MessagingService));

        private string MessageQueuePath { get; set; }

        public static string ClientQueuePathPrefix { get{ return "Client"; } }

        public MessagingService(string queuPath)
        {
            
            BasicConfigurator.Configure();
            MessageQueuePath = queuPath;
            CreateQueue();
        }

        /// <summary>
        /// Receive Messages from specified MessageQueue 
        /// </summary>
        /// <typeparam name="T">Body stream is of type T</typeparam>
        /// <returns>Object of type T</returns>
        public T Receive<T>() where T : new()
        {
            T result = new T();

            try
            {

                using (var ReceieveQueue = new MessageQueue(MessageQueuePath))
                {
                    ReceieveQueue.Formatter = new XmlMessageFormatter(new Type[] { typeof(T) });

                    var message = ReceieveQueue.Receive();

                    if (message != null && message.Body is T)
                    {
                        result = (T)message.Body;
                    }

                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
                return result;
        }


        /// <summary>
        /// Just Pings specified MessageQueue and returns true if any Message exists
        /// </summary>
        /// <returns>True or false</returns>
        public bool Receive()
        {
            bool hasMessageInQueue= false;
            try
            {
                using (var ReceiveQueue = new MessageQueue(MessageQueuePath))
                {
                    var message = ReceiveQueue.Receive();

                    if (message != null)
                    {
                        hasMessageInQueue = true;
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
            return hasMessageInQueue;
        }

        /// <summary>
        /// Send Messaqe to designated Queue
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="messageObject">Message object body</param>
        /// <param name="ttl">Message Time to Live in Seconds</param>
        public void Send<T>(T messageObject, int ttl)
        {
            try
            {
                using (var SendQueue = new MessageQueue(MessageQueuePath))
                {
                    Message message = new Message();
                    message.TimeToBeReceived = new TimeSpan(0, 0, ttl);
                    XmlSerializer serializer = new XmlSerializer(typeof(T));
                    serializer.Serialize(message.BodyStream, messageObject);

                    SendQueue.Send(message);
                }
            }

            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
        }

        /// <summary>
        /// Send Message to specified MessageQueue list
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="messageObject">Message object body</param>
        /// <param name="queuePath">List of Message Queues to Send Message</param>
        /// <param name="ttl">Message Time to Live in Seconds</param>
        public void Send<T>(T messageObject, List<string> queuePath, int ttl)
        {
            Message message = new Message();
            message.TimeToBeReceived = new TimeSpan(0, 0, ttl);
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            serializer.Serialize(message.BodyStream, messageObject);

            try
            {
                foreach (var client in queuePath)
                {
                    using (var SendQueue = new MessageQueue(client))
                    {
                        SendQueue.Send(message);
                    }
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
        }

        /// <summary>
        /// Creates and Sends Empty Message into specified MessageQueue
        /// </summary>
        /// <param name="ttl">Message Time to Live in Seconds</param>
        public void Send(int ttl)
        {
            Message message = new Message();
            message.TimeToBeReceived = new TimeSpan(0, 0, ttl);
            try
            {
                using (var SendQueue = new MessageQueue(MessageQueuePath))
                {
                    SendQueue.Send(message);
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
        }

        /// <summary>
        /// Finds Win clients which are currently active
        /// </summary>
        /// <param name="machinename">UNC name for MSMQ folders to scan</param>
        /// <returns></returns>
        public List<string> GetWinClientQueuePaths(string machinename)
        {
            List<string> activeclients = new List<string>();

           var queues= MessageQueue.GetPrivateQueuesByMachine(machinename);
           
            //perform search

           foreach (var queue in queues)
           {

               if (Regex.IsMatch(queue.QueueName, ClientQueuePathPrefix, RegexOptions.IgnoreCase))
               {
                   activeclients.Add(string.Format(".\\{0}", queue.QueueName));
               }
           }
           return activeclients;
        }

        /// <summary>
        /// Remove client MessageQueue when app exits
        /// </summary>
        public void DeleteQueue()
        {
            try
            {
                if (MessageQueue.Exists(MessageQueuePath))
                {
                    MessageQueue.Delete(MessageQueuePath);
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
        }

        private void CreateQueue()
        {
           try
            {
                if (!MessageQueue.Exists(MessageQueuePath))
                {
                    MessageQueue.Create(MessageQueuePath);

                }
            }
            catch (Exception ex)
            {
                logger.Error(ex.Message);
            }
        }
    }
}
